﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using XNAGame = Microsoft.Xna.Framework.Game;
using Microsoft.Xna.Framework.Graphics;
using FarseerGames.FarseerPhysics;
using Microsoft.Xna.Framework.Audio;
using FSVector2 = FarseerGames.FarseerPhysics.Mathematics.Vector2;
using CymbergajTouch.Logic;
using CymbergajTouch.Match;
using Microsoft.Xna.Framework.Input;
using CymbergajTouch.Menu;

namespace CymbergajTouch.Components
{
    /// <summary>
    /// XNA drawable component for Cymbergaj match.
    /// </summary>
    class MatchComponent : DrawableGameComponent
    {

        class PauseButtonMovable : Movable
        {
            private MatchComponent outerObject;
            public PauseButtonMovable(MatchComponent outerObject)
            {
                this.outerObject = outerObject;
            }

            bool Movable.IsTouched(FSVector2 position)
            {
                if (!outerObject.paused)
                {
                    float buttonRadius = Math.Min(IceRink.iceWidth, IceRink.iceHeight) / 30;
                    float dx = position.X - IceRink.iceWidth / 2;
                    float dy = position.Y - IceRink.iceHeight / 2;
                    return Math.Sqrt(dx * dx + dy * dy) < buttonRadius;
                }
                return false;
            }

            void Movable.Grab(FSVector2 inputPosition)
            {
                outerObject.ShowPauseMenu();
            }

            void Movable.Move(FSVector2 newPosition)
            {
                // ignore
            }

            void Movable.Drop()
            {
                // ignore
            }
        }

        //game objects
        IceRink iceRink;
        Puck puck;
        Pusher leftPusher;
        Pusher rightPusher;
        Pusher extraPusher;

        //XNA
        PhysicsSimulator physicsSimulator;

        //input
        private InputManager inputManager;
        private KeyboardInput keyboardInput;

        //meeting
        private MatchScore matchScore;

        //Sound
        AudioEngine audioEngine;
        WaveBank waveBank;
        SoundBank soundBank;

        //Pause
        private bool paused = false;
        private MenuComponent pauseMenu;

        //Options
        private Options options;

        //End of the game
        private bool finished = false;
        private MenuComponent finishedMatchMenu;

        //Bonus manager
        private BonusManager bonusManager;

        public MatchComponent(XNAGame game, KeyboardInput keyboardInput, Options options)
            : base(game)
        {
            this.keyboardInput = keyboardInput;
            this.options = options;

            pauseMenu = new MenuComponent(Game, keyboardInput);
            pauseMenu.AddItem(new MenuItem("Resume", HidePauseMenu));
            pauseMenu.AddItem(new MenuItem("Exit match", ExitMatch));

            finishedMatchMenu = new MenuComponent(Game, keyboardInput);
            finishedMatchMenu.AddItem(new MenuItem("Rematch", ((Cymbergaj)game).BeginMatch));
            finishedMatchMenu.AddItem(new MenuItem("Exit match", ExitMatch));

        }

        /// <summary>
        /// Initialization. Match objects are created.
        /// </summary>
        public override void Initialize()
        {
            inputManager = InputManager.Instance;

            physicsSimulator = new PhysicsSimulator(new FSVector2(0, 0));

            iceRink = new IceRink(physicsSimulator);

            leftPusher = new Pusher(physicsSimulator, new FSVector2(IceRink.iceWidth / 4, IceRink.iceHeight / 2), true);
            inputManager.AddMovable(leftPusher);

            rightPusher = new Pusher(physicsSimulator, new FSVector2(3 * IceRink.iceWidth / 4, IceRink.iceHeight / 2), false);
            inputManager.AddMovable(rightPusher);

            extraPusher = new Pusher(new FSVector2(0, 0));

            puck = new Puck(physicsSimulator, new FSVector2(IceRink.iceWidth / 2, IceRink.iceHeight / 2), options);
            inputManager.AddMovable(puck);

            inputManager.AddMovable(new PauseButtonMovable(this));

            puck.SetPushers(leftPusher, rightPusher, extraPusher);

            iceRink.SetCollisionCategories(leftPusher, rightPusher, puck);

            matchScore = new MatchScore(options);

            bonusManager = new BonusManager(this, options);
            bonusManager.SetPuck(puck);
            bonusManager.SetIceRink(iceRink);

            iceRink.GoalScored += new GoalEventHandler(matchScore.GoalScored);
            iceRink.GoalScored += new GoalEventHandler(puck.GoalScored);
            iceRink.GoalScored += new GoalEventHandler(bonusManager.GoalScored);


            base.Initialize();
        }

        /// <summary>
        /// Loads graphics content.
        /// </summary>
        protected override void LoadContent()
        {
            //Graphics
            puck.SetTexture(Game.Content.Load<Texture2D>("Images/puck"));
            puck.SetBonusTexture(Game.Content.Load<Texture2D>("Images/bonus-puck"));
            leftPusher.SetTexture(Game.Content.Load<Texture2D>("Images/pusher-blue"));
            rightPusher.SetTexture(Game.Content.Load<Texture2D>("Images/pusher-red"));
            extraPusher.SetTexture(Game.Content.Load<Texture2D>("Images/pusher-gold"));
            iceRink.SetIceTexture(Game.Content.Load<Texture2D>("Images/ice-rink"));
            iceRink.SetGoalTexture(Game.Content.Load<Texture2D>("Images/goal"));
            pauseMenu.SetBackgroundTexture(Game.Content.Load<Texture2D>("Images/black"));
            finishedMatchMenu.SetBackgroundTexture(Game.Content.Load<Texture2D>("Images/black"));
            matchScore.SetBackgroundTexture(Game.Content.Load<Texture2D>("Images/black"));
            bonusManager.SetTexture(Game.Content.Load<Texture2D>("Images/gold"));

            //Sound
            audioEngine = new AudioEngine("Content/Sounds/sounds.xgs");
            waveBank = new WaveBank(audioEngine, "Content/Sounds/WaveBank.xwb");
            soundBank = new SoundBank(audioEngine, "Content/Sounds/SoundBank.xsb");
            puck.SetSoundBank(soundBank);
            matchScore.SetSoundBank(soundBank);

            //Fonts
            matchScore.SetFont(Game.Content.Load<SpriteFont>("Fonts/scoreFont"));
            bonusManager.SetFont(Game.Content.Load<SpriteFont>("Fonts/scoreFont"));

            base.LoadContent();
        }


        /// <summary>
        /// Renders game objects.
        /// </summary>
        /// <param name="gameTime">XNA gameTime</param>
        public override void Draw(GameTime gameTime)
        {
            SpriteBatch spriteBatch = ((Cymbergaj)Game).SpriteBatch;

            spriteBatch.Begin();
            iceRink.Draw(spriteBatch);
            bonusManager.Draw(spriteBatch);
            puck.Draw(spriteBatch);
            leftPusher.Draw(spriteBatch);
            rightPusher.Draw(spriteBatch);
            if (physicsSimulator.BodyList.Contains(extraPusher.body))
            {
                extraPusher.Draw(spriteBatch);
            }
            matchScore.Draw(spriteBatch);
            spriteBatch.End();

            base.Draw(gameTime);
        }

        /// <summary>
        /// Updates game state.
        /// </summary>
        /// <param name="gameTime">point in time where game is</param>
        public override void Update(GameTime gameTime)
        {
            iceRink.CheckForGoals(puck.body.Position);
            bonusManager.Update(gameTime, paused);
            matchScore.Update(gameTime, paused);
            physicsSimulator.Update(gameTime.ElapsedGameTime.Milliseconds);
            applyRotationFriction();
            clearVectors();
            CheckKeyboardInput();
            audioEngine.Update();
            if (!finished && options.GoalsToWinValue != GoalsToWin.NoWinner)
            {
                if (matchScore.IsMatchFinished((int)options.GoalsToWinValue))
                {
                    FinishMatch();
                    ShowFinishedMatchMenu();
                }
            }
            base.Update(gameTime);
        }

        private void applyRotationFriction()
        {
            float friction = 0.003f;
            if (puck.body.AngularVelocity < 0)
            {
                puck.body.ApplyTorque(friction);
            }
            else if (puck.body.AngularVelocity > 0)
            {
                puck.body.ApplyTorque(-friction);
            }

            leftPusher.body.AngularVelocity = 0;
            rightPusher.body.AngularVelocity = 0;
            extraPusher.body.AngularVelocity = 0;
        }

        /// <summary>
        /// Checks if pause menu was invoked during the match.
        /// </summary>
        private void CheckKeyboardInput()
        {
            if (keyboardInput.IsNewKeyPressed(Keys.Escape))
            {
                if (paused)
                {
                    HidePauseMenu();
                }
                else
                {
                    ShowPauseMenu();
                }
            }
        }

        /// <summary>
        /// Pauses the match and shows pause menu.
        /// </summary>
        private void ShowPauseMenu()
        {
            PauseMatch();
            Game.Components.Add(pauseMenu);
            pauseMenu.Enabled = true;
        }

        /// <summary>
        /// Hides pause menu and unpauses the match.
        /// </summary>
        private void HidePauseMenu()
        {
            Game.Components.Remove(pauseMenu);
            pauseMenu.Enabled = false;
            UnpauseMatch();
        }

        /// <summary>
        /// Pauses the match.
        /// </summary>
        public void PauseMatch()
        {
            paused = true;
            clearVectors();
            physicsSimulator.Enabled = false;
        }

        private void clearVectors()
        {
            puck.ClearVector();
            leftPusher.ClearVector();
            rightPusher.ClearVector();
            extraPusher.ClearVector();
        }

        /// <summary>
        /// Unpauses the match.
        /// </summary>
        public void UnpauseMatch()
        {
            paused = false;
            physicsSimulator.Enabled = true;
        }

        /// <summary>
        /// Shows finished match menu.
        /// </summary>
        private void ShowFinishedMatchMenu()
        {
            finishedMatchMenu.Enabled = true;
            Game.Components.Add(finishedMatchMenu);
        }

        /// <summary>
        /// Adds extra pusher to match.
        /// </summary>
        /// <param name="left">goal scored side</param>
        public void AddExtraPusher(bool left)
        {
            extraPusher.left = left;
            if (left)
            {
                extraPusher.body.Position = new FSVector2(IceRink.iceWidth / 4, IceRink.iceHeight / 4);
            }
            else
            {
                extraPusher.body.Position = new FSVector2(3 * IceRink.iceWidth / 4, IceRink.iceHeight / 4);
            }
            physicsSimulator.Add(extraPusher.body);
            physicsSimulator.Add(extraPusher.geom);
            physicsSimulator.ProcessAddedAndRemoved();
            inputManager.AddMovable(extraPusher);
            iceRink.SetExtraPusherCollisionCategory(extraPusher);
        }

        /// <summary>
        /// Removes extra pusher from ice.
        /// </summary>
        public void RemoveExtraPusher()
        {
            physicsSimulator.Remove(extraPusher.body);
            physicsSimulator.Remove(extraPusher.geom);
            physicsSimulator.ProcessAddedAndRemoved();
            inputManager.RemoveMovable(extraPusher);
        }

        /// <summary>
        /// Makes pusher big.
        /// </summary>
        /// <param name="left">goal scored side</param>
        public void EnlargePusher(bool left)
        {
            if (left)
            {
                leftPusher.Enlarge();
            }
            else
            {
                rightPusher.Enlarge();
            }
        }

        /// <summary>
        /// Makes pusher small.
        /// </summary>
        /// <param name="left">goal scored side</param>
        public void ReducePusher(bool left)
        {
            if (left)
            {
                leftPusher.Reduce();
            }
            else
            {
                rightPusher.Reduce();
            }
        }

        /// <summary>
        /// Makes pusher's size normal.
        /// </summary>
        /// <param name="left">goal scored side</param>
        public void NormalizePusher(bool left)
        {
            if (left)
            {
                leftPusher.Normalize();
            }
            else
            {
                rightPusher.Normalize();
            }
        }

        /// <summary>
        /// Adds gravity to ice rink in order to favour one side.
        /// </summary>
        /// <param name="left">goal scored side</param>
        public void AddGravity(bool left)
        {
            FSVector2 gravity;
            if (left)
            {
                gravity = new FSVector2(-0.0002f, 0);
            }
            else
            {
                gravity = new FSVector2(0.0002f, 0);
            }
            physicsSimulator.Gravity = gravity;
        }

        /// <summary>
        /// Clears ice rink's gravity.
        /// </summary>
        public void ClearGravity()
        {
            physicsSimulator.Gravity = new FSVector2(0, 0);
        }

        /// <summary>
        /// Exits from current match.
        /// </summary>
        private void ExitMatch()
        {
            FinishMatch();
            ((Cymbergaj)Game).ShowMenu();
        }

        /// <summary>
        /// Finishes current match.
        /// </summary>
        private void FinishMatch()
        {
            finished = true;
            PauseMatch();
            matchScore.Finish();
        }
    }
}
